/*
 * Copyright (C) 2020 The LINN Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.juqimiao.linn.interception;

import com.juqimiao.linn.context.ApplicationContextWrapperI;
import com.juqimiao.linn.extension.ExtensionRegistry;
import com.juqimiao.linn.interception.config.InterceptorProperties;
import com.juqimiao.linn.logging.Logger;
import com.juqimiao.linn.registry.RegistryI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * 拦截器注册类。拦截器注册过程优先于其他模块注册过程。
 * <p>
 *     注册器会注册在下面配置中实现了拦截器标识接口的实现类，这些实现类会通过
 *     {@link PreInterceptor} 或/并 {@link PostInterceptor}注解来区分是
 *     需要被注册为前置拦截器还是后置拦截器。
 * </p>
 * <p>
 *     根据不同的接口，识别拦截器用于拦截相应类中的方法。
 *     拦截器接口通过
 *     linn:
 *      config:
 *       interception:
 *        interceptors:
 *         - com.juqimiao.linn.command.CommandInterceptorI
 *         - com.juqimiao.linn.xxxx.xxxxxInterceptorI
 *
 * 当不配置是，拦截器将不会生效。注册过程也不会注册任何拦截器信息。
 * </p>
 *
 * @author Collin Cai
 */
@Component("interceptor")
@Order(524)
public class InterceptorRegistry implements RegistryI {

    /* ---- Fileds ---------- */

    /**
     * ApplicationContext的代理对象。
     */
    @Autowired
    private ApplicationContextWrapperI applicationContextWrapper;

    /**
     * 拦截器配置属性。
     * linn:
     * config:
     * interception:
     * interceptors:
     * - com.juqimiao.linn.command.CommandInterceptorI
     * - com.juqimiao.linn.xxxx.xxxxxInterceptorI
     */
    private InterceptorProperties interceptorProperties;

    /**
     * 拦截器维持单元。
     */
    @Autowired
    private InterceptorKeeperI interceptorRepository;

    /**
     * 日志。
     */
    private Logger logger = Logger.getLogger(ExtensionRegistry.class);

    /* ---- Constructor ---------- */

    /**
     * 构造器。
     *
     * @param interceptorProperties 拦截器配置属性
     */
    @Autowired
    public InterceptorRegistry(InterceptorProperties interceptorProperties) {
        this.interceptorProperties = interceptorProperties;
    }

    /* ---- Methods ---------- */

    /**
     * 拦截器注册过程，其注册次序优先于其他注册单元。
     */
    @Override
    public void register() {
        //从配置中读取支持interceptor的接口定义Class。
        Class[] interceptorClzes = interceptorProperties.getInterceptors();
        if (interceptorClzes != null
                && interceptorClzes.length > 0) {
            //通过支持interceptor的接口定义Class获取所有的bean。
            //通过读取每个Interceptor的接口的定义是否有PreInterceptor或PostInterceptor注解
            //分别一个每个Bean的class name为 key 分别保存到GlobalPreInterceptors 和 GlobalPostInterceptors Map中。
            Arrays.stream(interceptorClzes).forEach(interceptorClz -> {
                logger.info(String.format("Begin register interceptor interface : %s", interceptorClz));
                collectInterceptors(interceptorClz);
                logger.info(String.format("End register interceptor interface : %s", interceptorClz));
            });

            //Arrays.sort(interceptors, Comparator.comparingInt(t -> t.getOrder()));
        }
    }

    /**
     * 根据不同的拦截器标识接口进行分类，并分别存储PreInterceptor 和 PostInterceptor.
     *
     * @param interceptorClz 拦截器标识接口。
     */
    private void collectInterceptors(Class<InterceptorI> interceptorClz) {
        Map<String, InterceptorI> interceptors = applicationContextWrapper.getBeansOfType(interceptorClz);
        if (interceptors != null && interceptors.size() > 0) {
            interceptors.forEach((key, interceptor) -> {
                Annotation[] annotations = interceptor.getClass().getAnnotations();
                Arrays.stream(annotations).forEach(annotation -> {
                    String clzName = interceptorClz.getName();
                    if (annotation instanceof PreInterceptor) {
                        Map<String, List<InterceptorI>> preInterceptors = interceptorRepository.getPreInterceptors();
                        if (!preInterceptors.containsKey(clzName)) {
                            logger.info(String.format("Register PreInterceptor : %s", clzName));
                            preInterceptors.put(clzName, new ArrayList<>());
                        }
                        preInterceptors.get(clzName).add((InterceptorI) interceptor);
                    }
                    if (annotation instanceof PostInterceptor) {
                        Map<String, List<InterceptorI>> postInterceptors = interceptorRepository.getPostInterceptors();
                        if (!postInterceptors.containsKey(clzName)) {
                            logger.info(String.format("Register PostInterceptor : %s", clzName));
                            postInterceptors.put(clzName, new ArrayList<>());
                        }
                        postInterceptors.get(clzName).add((InterceptorI) interceptor);
                    }
                });

            });
        }
    }
}
